1. Field of the Invention
This invention relates generally to microprocessors and more specifically to a microprocessor having circuitry to assist in debugging a computer program by providing a means for precisely locating errors in the program.
2. Prior Art
When a computer program is executed on a microprocessor, the program may not function correctly. For example, the program may provide some results which are correct and other results which are incorrect; or the program may execute for a period of time but then abnormally terminate execution. Assuming that the hardware is operating correctly, the cause of the erroneous behavior in each of the examples is usually an error, a bug, in the computer program. Finding a computer program error often requires considerable effort because microprocessors lack a means for automatically identifying the location of the program step or steps which produce the erroneous results.
To identify the location of the bug, the programmer must first identify the area of the program in which the bug is likely to have occurred. The programmer must then test whether the identified area of the computer program includes the bug. If the identified area includes the bug, the programmer divides and further subdivides that area of the program until the precise location of the bug is found.
Locating a bug is further complicated by the fact that steps in a computer program are often executed repeatedly, possibly millions of times in each execution of the program, and the bug may manifest itself only on rare occasions. In such a situation, locating a bug involves not only finding the program line or lines which contain the error but also determining which execution of the program line is responsible for the problem.
One common approach to isolating a bug is to insert debugging print statements into the computer program. In this approach, the programmer has the program print information that seems relevant and useful at selected locations in the program so that when an error occurs the general location of the error within the program can be identified. However, debugging print statements are inefficient because the location of an error is not identified mechanically, but rather through considerable human effort and intelligence. Moreover, this approach involves a time-consuming modification of the defective program and as a result of the modifications, the symptoms of the bug may change or even disappear.
In many high level language programming environments and in most assembly language programming environments, a computer program, referred to as an on-line debugger (debugger), is available to allow interactive debugging of the user's program. The on-line debugger uses specialized facilities of the microprocessor, described below, to generate information to aid in identifying a bug. A debugger usually provides facilities, such as single stepping and breakpoints, that allow the programmer to stop a program and subsequently examine or modify the contents of registers and memory of the microprocessor. Sophisticated debuggers allow programmers to use the same symbolic names that the program source uses to examine and modify data values in memory.
With single stepping, the user program is executed one instruction at a time, with control returning to the debugger after each step. After each program step, the debugger can request the programmer to specify what he wants to do next, but often the debugger is configured to examine memory or register values and make decisions about whether to print out such information or to request further instructions from the programmer's keyboard. Even with such automatic actions on the part of the debugger, however, single-stepping tends to be a very slow mode of operation. For example, a system operating at 1 million instructions per second may be slowed to perhaps a thousand user-program instructions per second. With single stepping, a program that failed after 30 seconds of normal operation takes eight hours to fail.
Breakpoints offer an alternative to single stepping. Breakpoints allow a program to be executed at full speed until a particular instruction, a breakpoint, in the program is reached. Since a breakpoint is set by the programmer, the programmer must select, in advance, where execution is to stop. Setting breakpoints skillfully is an art that a programmer learns through experience.
Often, breakpoints are used in conjunction with single stepping to debug a program. A programmer first runs the program to understand the symptoms of the error. The programmer makes an educated guess, based on those symptoms, as to where in the program the bug is likely to be located. The debugger is then used to set one or more breakpoints in an effort to cause the execution of the program to stop just before the program enters the suspected area (or areas). When the program is run again, if the bug is observed to occur before a breakpoint is reached, new breakpoints must be set and the whole process repeated. Considerable skill and effort is often expended just to stop the program a few instructions prior to where it would terminate as a result of a bug in the code.
If, however, a breakpoint causes execution to stop before the bug occurs, the debugger can be used to examine registers and memory in an effort to determine whether the values are reasonable and/or correct. At this point the program can be single stepped through any number of instructions and the values in memory and registers examined in an effort to understand the program's behavior and to locate the bug. There is, however, no guarantee that only some small and manageable number of instructions remain to be executed before the symptoms of the bug appear. If single stepping does not reveal the cause of the bug, the breakpoints must be moved and the entire process repeated. Alternatively, if data is simply printed at each step, an enormous amount of data may have to be studied before the bug is discovered. In either case, human effort and ingenuity is required.
Most modern microprocessors support the previously described methods of debugging. For example, the .mu.PD70616(V60) provides three independent hardware aids to support debuggers. These aids are (i) instruction breakpoints, (ii) instruction trace and (iii) address traps. Typically, the instruction breakpoint facility is used to implement program flow debugging, as previously described. An instruction breakpoint is set by replacing the first byte of an instruction with the one byte "BRK" instruction. The program executes at full speed until the BRK instruction is executed. The BRK instruction causes a breakpoint trap to occur. When the breakpoint trap occurs, the state of the microprocessor is saved to memory and the processor begins execution of software, the breakpoint trap handler, that is part of the debugger. Since the previous state of the microprocessor including register values has been saved to memory that is accessible by the debugger, the debugger can examine, and even modify, the previous state of the microprocessor as represented in memory. Either the previous state, or some modification of the previous state, can be loaded from memory into the microprocessor and execution of the user's program resumed.
In the V60, the instruction tracing mechanism supports single stepping, as previously described. A field in one of the microprocessor's registers, the program status word (PSW) as illustrated in FIG. 1, is used to control instruction tracing. When instruction tracing is enabled, a trace trap occurs after each instruction execution. When a trace trap occurs, the microprocessor's behavior is much the same as when a breakpoint trap occurs and with the same purpose of allowing the debugger program to intervene.
The V60 microprocessor supports another debugging facility, one which is not found on other microprocessors. Address traps, when they occur, are similar in behavior to trace traps and to breakpoint traps. In each case the objective is to suspend the user's program so that the debugger can execute until the debugger later allows the user's program to resume. The distinction between these three kinds of traps lies primarily in what initiates the traps. Trace traps (when enabled) occur after each instruction whereas breakpoint traps occur whenever the V60 executes a BRK instruction. Address traps, in contrast, occur (if enabled by a bit in the PSW) whenever the V60 initiates a specified kind of memory reference. A debugger specifies which memory references are to cause address traps by configuring registers in the microprocessor, specifically, registers identified as ADTRO, ADTR1, ADTMRO, ADTMR1 and TRMOD.
The V60 generally initiates several memory references for each instruction that it executes. First, the instruction must be read from memory. This memory reference is called an "execute access". Also, a data value must often be read from memory in a "read access". Similarly, a data value must often be written to memory in a "write access". The TRMOD register is used to specify whether read, write or execute accesses are to be candidates for generating address traps.
A V60 program identifies, through a 32-bit "virtual address", which location in memory a given memory access affects. The ADTRO, ADTR1, ADTMRO and ADTMR1 registers are used to specify which virtual address or addresses are to generate address traps. One range of virtual addresses can be specified with the register pair ADTRO, ADTMRO and another with the registers ADTR1, ADTMR1.
In brief, when address traps are enabled, an address trap will only occur if the V60 initiates a memory access of a type (read, write or execute) enabled by bits in the TRMOD register at a virtual address which matches the address specified by ADTRO on at least all of the bits that are set to 1 in the ADTRMO register. When address traps are enabled, an address trap occurs if the V60 initiates a memory access of an enabled type at a virtual address which matches the address specified by ADTR1 on all of the bits that are set to 1 in the ADTRM1 register. By using address traps, a debugger can allow a program to run at full speed until, for example, a write is initiated to some predetermined address.
The mechanisms, described above, wherein an executing program (task) is suspended, the microprocessor state saved, and another code (a trap handler) executed, are general mechanisms that the V60 shares with many other microprocessors. An almost identical mechanism exists to invoke an interrupt handler (rather than a trap handler). The difference is that an interrupt is initiated by a hardware signal from another device external to the microprocessor. A trap handler is invoked by an event, usually caused by software, that is internal to the microprocessor. Traps often occur because bugs in an executing program request the microprocessor to perform an illegal operation, but in some instances, such as in debugging, traps are intentionally initiated by software. Interrupts are typically used to cause a computing system to respond to some physical event such as a clock tick. The term "exception" is often used to refer to either a trap or interrupt.
FIG. 2 shows the system base table for the V60, which is stored in memory. The V60 uses the system base table to invoke either a software trap or an interrupt handler. Specifically, the V60 determines which entry in the system base table is appropriate either from the nature of the event in the case of traps, or from a data value called an interrupt vector which hardware external to the V60 presents to the V60 data pins (much as data from memory would be presented in other circumstances).
The V60 is intended for use in a multi-tasking computer system in which operating system software supervises the activities of one or more user tasks. In this kind of system, it is necessary to restrict what user tasks are allowed to do so that one user task cannot interfere with the work of another user task. The V60 makes this possible through the use of "execution levels". Critical parts of the operating system are allowed to assume execution level 0, which removes all constraints on what software is allowed to do. At execution levels that are assigned higher numbers, some instructions are disallowed and access to various regions of memory becomes restricted. Generally, more restrictions are placed on programs as the number of the execution level increases. How different execution levels are used in a given computer system is very much an option of the operating system designer. In general, however, the most critical parts of the operating system, sometimes referred to as the kernel of the operating system, execute at level 0 and user programs execute at a more restricted, higher numbered, level. Probably the most common use of execution levels includes only two execution levels. With two execution levels, all programs but the critical parts of the operating system run at the higher numbered (more restricted) execution level.
The program status word (PSW) of the .mu.PD70616(V60) is shown in FIG. 1 as an example of a typical microprocessor PSW. The PSW is divided into four 8-bit fields: an integer field, 40; a floating point field, 50; a control field, 60; and a task status field, 70. It should be observed that the execution level itself is part of the task status field, bits 24-25. Also in the upper half of the PSW is bit 16, which enables trace traps, and bit 17, which enables address traps. Bits 4-7, 13-15 and 19-23 of the PSW are unused.
The upper half of the PSW is vital to the security of a computer system based on the V60. For this reason, the instruction to write the upper half of the PSW register can be executed only when the V60 is operating at execution level 0. The other way that the upper half of the PSW can be changed (in fact, the way that the execution level typically becomes 0) is when the V60 enters an exception handler. The execution level that an exception handler assumes is specified in the system base table (See FIG. 2).
While many microprocessors support some aids to debugging such as have been described above, no existing microprocessor provides a direct means for determining the location of a program error. Hence, the programmer must expend considerable effort just to locate such a software error.